'''
Created on 18 juin 2013

@author: Nils Amiet
'''

from ITInfluence.models import Tweet, User, Friendship
import networkx as nx
import math

class TwitterFollowersGraphBuilder():
    '''
    Builds the followers graph
    '''
    
    def __init__(self):
        self.graph = nx.DiGraph()
        self.nodeSizes = []
        self.nodeFriendCounts = []
        
        print("Building graph...")
        print("Running user query...")
        maxFollowersCount = 500
        minFollowersCount = 5000
        ### You can uncomment one of the following lines with "users = ..." at your choice
        # All users that have at most 500 followers
        users = User.objects.all().filter(followers_count__lte=maxFollowersCount).exclude(followers_count=0)
        # All users that have at least 5000 followers
#         users = User.objects.all().filter(followers_count__gte=minFollowersCount).exclude(followers_count=0)
        # All users than have at least one follower
#         users = User.objects.all().exclude(followers_count=0)
        # Don't run this one if you have less than 16GB memory on large networks (>10M)
#         users = User.objects.all()
        print("done!")

        self.buildGraph(users)
        print("...done building graph!")
        print("Processed %s users" % len(users))
        
    def buildGraph(self, users):
        print("Counting users...")
        userCount = len(users)
        print("done!")
        counter = 0
        for user in users:
            percentage = 100*(float(counter)/userCount)
            print("Processing user %s/%s (%s" %(counter, userCount, percentage) + "%)")
            userId = int(user.id)
            self.graph.add_node(userId)
            userWeight = self.getWeight(int(user.followers_count))
            
            friendships = Friendship.objects.all().filter(user__id=int(user.id))
            
            friendshipCount = friendships.count()
            friendshipCounter = 0
            
            for friendship in friendships:
                percentage = 100*(float(friendshipCounter)/friendshipCount)
                print("Processing friendship %s/%s (%s" %(friendshipCounter, friendshipCount, percentage) + "%)"+ " - User: %s" % percentage + "%")
                self.graph.add_edge(userId, int(friendship.followed_user.id))
                friendshipCounter += 1
        
            counter += 1
        
        nodeCount = len(self.graph.nodes())
        counter = 1
        for node in self.graph.nodes():
            percentage = 100*(float(counter)/nodeCount)
            print("Processing node %s/%s (%s" %(counter, nodeCount, percentage) + "%)")
            user = User.objects.get(id=node)
            userWeight = self.getWeight(int(user.followers_count))
            self.nodeSizes += [userWeight]
            
            userFriendsCount = self.graph.out_degree(node)
            self.nodeFriendCounts += [userFriendsCount]
            
            counter += 1
                    
    def getWeight(self, followers):
        nodeSize = 25
        logBase = 10
        return nodeSize + 4 ** math.log(followers, logBase)





class TwitterGraphBuilder():
    '''
    Builds the replies graph for collected Twitter data
    '''

    def __init__(self, hashtagOfInterest="summer"):
        '''
        Constructor
        '''
        self.graph = nx.DiGraph()
        
        print("Building graph...")
        print("Running tweet query...")
        ### Uncomment one of the following lines "tweets = ..."
        # Use a not so frequent hashtag, fast computation
        tweets = Tweet.objects.all().filter(hashtags__icontains=hashtagOfInterest)
        # Don't run this if you have less than 32GB memory for large networks (>10M).
#         tweets = Tweet.objects.all()
        self.buildGraph(tweets)
        print("...done building graph!")
        
    def buildGraph(self, tweets):
        tweetCount = len(tweets)
        counter = 0
        for tweet in tweets:
            percentage = 100*(float(counter)/tweetCount)
            print("Processing tweet %s/%s (%s" %(counter, tweetCount, percentage) + "%)")
            author = tweet.user
            replyTo = int(tweet.in_reply_to_status_id)
            
            if replyTo is 0:
                # it's a reply to all of the author's followers
                followers = self.getFollowers(author)
                
                followersCount = len(followers)
                followerCounter = 0
                for follower in followers:
                    followerPercentage = 100*(float(followerCounter)/followersCount)
                    print("Processing follower %s/%s (%s" %(followerCounter, followersCount, followerPercentage) + "%)" + " - Tweet: %s" % percentage + "%")
                    self.addReply(author, follower.user)
                    
                    followerCounter += 1
            else:
                try:
                    toUser = User.objects.get(replyTo)
                    self.addReply(author, toUser)
                except:
#                     print("Warning: in_reply_to user doesn't exist")
                    pass
            
            counter += 1
                
    def getFollowers(self, user):
        friendships = Friendship.objects.all().filter(followed_user__id=int(user.id))
        return friendships
    
    def addReply(self, fromUser, toUser):
        self.graph.add_edge(int(fromUser.id), int(toUser.id))